﻿

var CanvasGraph = function () {

    var ctx,
        graphHeight, graphWidth, yMin, yMax, xMin, xMax,
        xScale, yScale,
        functionInfo, canvasInfo,
        canvasRendered,
        ratio = 0,
        dataPoints = [],

    render = function (canvasID, canvInfo, functionObj) {
        functionInfo = functionObj;
        canvasInfo = canvInfo;

        var canvas = document.getElementById(canvasID);
        graphHeight = canvas.getAttribute("height");
        graphWidth = canvas.getAttribute("width");
        ctx = canvas.getContext("2d");

        canvasRendered = canvasInfo.wasRendered ? true : false;

        yMin = canvasInfo.yMin;
        yMax = canvasInfo.yMax;
        xMin = canvasInfo.xMin;
        xMax = canvasInfo.xMax;

        yScale = graphHeight / (yMax - yMin);
        xScale = graphWidth / (xMax - xMin);

        xStep = functionInfo ? functionInfo.step * (xMax - xMin) / graphWidth : null;

        if (!canvasRendered) {
            ctx.fillStyle = canvasInfo.canvasColor ? canvasInfo.canvasColor : "rgba(255,255,255,1)";
            ctx.fillRect(0, 0, graphWidth, graphHeight);
        }

        ctx.save();
        ctx.translate(
            (-xMin) / (xMax - xMin) * graphWidth,
            (yMax) / (yMax - yMin) * graphHeight
            );

        renderParts();
        ctx.restore();
        dataPoints = [];
        canvasInfo.wasRendered = true;
    },

    processFunction = function () {
        var count = 0;
        var x = xMin - xStep;
        while (x <= xMax + xStep) {
            dataPoints.push({ x: x, y: functionInfo.graphFunction(x) });
            x += xStep;
            ++count;
        }
    },

    drawPoints = function () {
        var pointSize = functionInfo.pointSize ? functionInfo.pointSize : 2;
        for (var i = 0; i < dataPoints.length; ++i) {
            var pt = dataPoints[i];
            drawPoint(pt.x, pt.y, functionInfo.pointColor, pointSize);
        }
    },

    drawLines = function () {
        var prevPt = dataPoints[0];
        var lineWidth = functionInfo.lineWidth ? functionInfo.lineWidth : 1;

        for (var i = 1; i < dataPoints.length; ++i) {
            var currPt = dataPoints[i];
            drawLine({ x1: prevPt.x, y1: prevPt.y, x2: currPt.x, y2: currPt.y }, functionInfo.lineColor, lineWidth);
            prevPt = currPt;
        }
    }

    cartToCoord = function (pt) {
        return { x: pt.x * xScale, y: -pt.y * yScale };
    },

    renderParts = function () {
        if (!canvasRendered) {
            drawGuide(canvasInfo.backgroundStyle ? canvasInfo.backgroundStyle : "ticks");
            drawAxis();
        }

        if (!functionInfo) {
            return;
        }
        processFunction();

        if (functionInfo.lineColor) {
            drawLines();
        }
        if (functionInfo.pointColor) {
            drawPoints();
        }
    },

    drawPoint = function (x, y, color, size) {

        var pt = cartToCoord({ x: x, y: y });
        ctx.beginPath();
        ctx.arc(pt.x, pt.y, size ? size : 3, 0, 2 * Math.PI, true);
        ctx.fillStyle = color ? color : "red";
        ctx.fill();
        ctx.closePath();
    },

    drawOrigin = function () {
        drawPoint(0, 0, "rgba(0,0,0,0.5)", 4);
        drawPoint(0, yMax, "rgba(0,0,0,0.5)", 4);
        drawPoint(0, yMin, "rgba(0,0,0,0.5)", 4);
        drawPoint(xMin, 0, "rgba(0,0,0,0.5)", 4);
        drawPoint(xMax, 0, "rgba(0,0,0,0.5)", 4);

    },

    drawGuide = function (format) {

        var yBot, yTop, xLeft, xRight;

        if (format == "grid") {
            yBot = yMin;
            yTop = yMax;
            xLeft = xMin;
            xRight = xMax;
        }
        else {
            yBot = -0.25;
            yTop = 0.25;
            xLeft = -0.25;
            xRight = 0.25;
        }

        var x = Math.ceil(xMin);
        var y = Math.ceil(yMin);

        var guideColor = canvasInfo.guideColor ? canvasInfo.guideColor : "rgba(0,0,0,0.2)";
        var guideWidth = canvasInfo.guideWidth ? canvasInfo.guideWidth : 1;

        while (x <= Math.floor(xMax)) {
            drawLine({ x1: x, y1: yBot, x2: x, y2: yTop }, guideColor, guideWidth);
            ++x;
        }

        while (y <= Math.floor(yMax)) {
            drawLine({ x1: xLeft, y1: y, x2: xRight, y2: y }, guideColor, guideWidth);
            ++y;
        }
    },



    drawAxis = function () {
        //drawOrigin();
        var axisColor = canvasInfo.axisColor ? canvasInfo.axisColor : "rgba(0,0,0,0.5)";
        var axisWidth = canvasInfo.axisWidth ? canvasInfo.axisWidth : 1;
        drawLine({ x1: xMin, y1: 0, x2: xMax, y2: 0 }, axisColor, axisWidth);
        drawLine({ x1: 0, y1: yMin, x2: 0, y2: yMax }, axisColor, axisWidth);
    },

    drawLine = function (pt, strokeStyle, lineWidth) {

        var pt1 = cartToCoord({ x: pt.x1, y: pt.y1 });
        var pt2 = cartToCoord({ x: pt.x2, y: pt.y2 });

        ctx.strokeStyle = strokeStyle ? strokeStyle : "black";
        ctx.lineWidth = lineWidth ? lineWidth : 1;
        ctx.lineCap = "butt";
        ctx.miterLimit = 0;
        ctx.beginPath();
        ctx.moveTo(pt1.x, pt1.y);
        ctx.lineTo(pt2.x, pt2.y);
        ctx.stroke();
        ctx.closePath();
    },
    saveImage = function () {
        var url = canvas.toDataURL();
        var win = window.open(url);
    };

    return {
        render: render,
        saveImage: saveImage
    };
};